home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 17 / CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso / CUCD / Programming / DiceSource / master / Examples / Visual / VMake / file.c < prev    next >
C/C++ Source or Header  |  1994-02-01  |  21KB  |  721 lines

  1. #include "vmake.h"
  2.  
  3. Prototype int get_filename(char *str, char *buf, int savemode);
  4. Prototype void commit_filename(char *buf);
  5. Prototype void read_script(void);
  6. Prototype int read_file(void);
  7. Prototype int write_file(void);
  8. Prototype void expand_filename(char *name, char *fullname);
  9. Prototype void go_dir(BPTR dirlock);
  10. Prototype BPTR xlockdir(char *path);
  11.  
  12. #define LINE_LEN  78
  13. #define MIN_LINE  10
  14.  
  15. static BPTR XSaveLock;
  16. static BOOL XSaveLockValid;
  17.  
  18. /***********************************************************************************
  19.  * Procedure: restorecurdir
  20.  * Synopsis:  (void)restorecurdir();  - Called at exit time if necessary
  21.  * Purpose:   Restores the original directory from where the program started
  22.  ***********************************************************************************/
  23. static void restorecurdir()
  24. {
  25.    if (XSaveLockValid)
  26.       UnLock(CurrentDir(XSaveLock));
  27.    XSaveLockValid = 0;
  28. }
  29.  
  30. /***********************************************************************************
  31.  * Procedure: go_dir
  32.  * Synopsis:  (void)go_dir(dirlock);
  33.  * Purpose:   Set the current directory to one specified by the given lock
  34.  ***********************************************************************************/
  35. void go_dir(BPTR dirlock)
  36. {
  37.    BPTR lock;
  38.  
  39.    if (dirlock)
  40.    {
  41.       if ( (lock = DupLock(dirlock)) != NULL)
  42.       {
  43.          if (!XSaveLockValid)
  44.          {
  45.             XSaveLock = CurrentDir(lock);
  46.             XSaveLockValid = 1;
  47.             atexit(restorecurdir);
  48.          }
  49.          else
  50.          {
  51.             UnLock(CurrentDir(lock));
  52.          }
  53.       }
  54.    }
  55. }
  56.  
  57. BPTR xlockdir(char *path)
  58. {
  59.    BPTR lock;
  60.  
  61.    lock = Lock(path, SHARED_LOCK);
  62.  
  63.    /* We should check to make sure that the lock is valid */
  64.    return(lock);
  65. }
  66.  
  67. /***********************************************************************************
  68.  * Procedure: get_filename
  69.  * Synopsis:  type = get_filename(str)
  70.  * Purpose:   Parse out a file descriptor and return a type for that name
  71.  *            This routine does not modify any permanent global data
  72.  ***********************************************************************************/
  73. int get_filename(char *str, char *buf, int savemode)
  74. {
  75.    char *p;
  76.    int len;
  77.  
  78.    go_dir(global.homedir);
  79.  
  80.    if (*str)
  81.    {
  82.       /* They actually specified a name.  All we need to do is replace the */
  83.       /* current one.  If they specified a '?', then we need to prompt the */
  84.       /* user with the file requester.  We try to use ASL first and then   */
  85.       /* fall back to ARP.                                                 */
  86.       if (*str == '?')
  87.       {
  88.          int n;
  89.  
  90.          if (global.inrexx)
  91.          /* don't put up requesters if called from rexx - just fail */
  92.          {
  93.             global.rexxrc = TEXT_BADPROJ;  /* sort of fits the bill... */
  94.             return 0;
  95.          }
  96.  
  97.          if (global.freq == NULL) return(0);
  98.  
  99.          if (AslBase != NULL)
  100.          {
  101.             struct TagItem taglist[10];
  102.  
  103.             n = 0;
  104.             taglist[n  ].ti_Tag  = ASL_Pattern;
  105.             taglist[n++].ti_Data = (ULONG)global.text[CONFIG_PATTERN];
  106.  
  107.             taglist[n  ].ti_Tag  = ASL_File;
  108.             taglist[n++].ti_Data = (ULONG)"";
  109.  
  110.             taglist[n  ].ti_Tag  = ASL_Dir;
  111.             taglist[n++].ti_Data = (ULONG)"";
  112.  
  113.             taglist[n  ].ti_Tag  = ASL_Window;
  114.             taglist[n++].ti_Data = (ULONG)global.window;
  115.  
  116. #ifdef ASLFR_DoSaveMode
  117.             taglist[n  ].ti_Tag  = ASLFR_DoSaveMode;
  118.             taglist[n++].ti_Data = savemode;
  119. #endif
  120.  
  121. #ifdef ASLFR_DoPatterns
  122.             taglist[n  ].ti_Tag  = ASLFR_DoPatterns;
  123.             taglist[n++].ti_Data = TRUE;
  124. #endif
  125.  
  126. #ifdef ASLFR_RejectIcons
  127.             taglist[n  ].ti_Tag  = ASLFR_RejectIcons;
  128.             taglist[n++].ti_Data = TRUE;
  129. #endif
  130.  
  131. #ifdef ASLFR_SleepWindow
  132.             taglist[n  ].ti_Tag  = ASLFR_SleepWindow;
  133.             taglist[n++].ti_Data = TRUE;
  134. #endif
  135.  
  136.             taglist[n  ].ti_Tag  = TAG_DONE;
  137.             taglist[n++].ti_Data = 0;
  138.  
  139.             if (!AslRequest( (APTR)global.freq, taglist))
  140.                return(0);
  141.  
  142.          }
  143.          else
  144.          {
  145.             /* We must have arp.library (otherwise we wouldn't have a file     */
  146.             /* Requester structure.)                                           */
  147.             if (!ArpFileRequest( global.freq ))
  148.                return(0);
  149.          }
  150.  
  151.          /* Since the ASL requester and the ARP requester have the pointers to */
  152.          /* the directory and the filename in the same place, we can use the   */
  153.          /* same code to parse both of them                                    */
  154.          n = strlen(global.freq->rf_Dir);
  155.          len = strlen(global.freq->rf_File);
  156.          if (len == 0)
  157.             return (0);  /* didn't pick a name */
  158.          if (n > MAX_FILENAME) n = MAX_FILENAME;
  159.          p = buf;
  160.          memcpy(p, global.freq->rf_Dir, n);
  161.          p += n;
  162.  
  163.          if ((n > 0) && (p[-1] != ':'))
  164.          {
  165.             *p++ = '/';
  166.             n++;
  167.          }
  168.          if ((n + len) > MAX_FILENAME) len = MAX_FILENAME-n;
  169.          strncpy(p, global.freq->rf_File, len);
  170.          p[len] = 0;
  171.       }
  172.       else
  173.       {
  174.          /* Copy over the file name.  Stop when we get to a '?' */
  175.          int len;
  176.          p = strchr(str, ';');
  177.          if (p)
  178.             len = p-str;
  179.          else
  180.             len = strlen(str);
  181.          if (len > MAX_FILENAME) len = MAX_FILENAME;
  182.          memcpy(buf, str, len);
  183.          buf[len] = 0;
  184.       }
  185.    }
  186.    else
  187.    {
  188.       /* default is to use existing global.filename */
  189.       strcpy(buf, global.filename);
  190.    }
  191.    return(1);
  192. }
  193.  
  194. /***********************************************************************************
  195.  * Procedure: commit_filename
  196.  * Synopsis:  void get_filename(str)
  197.  * Purpose:   Set the global data to use a new file name (from get_filename())
  198.  ***********************************************************************************/
  199. void commit_filename(char *buf)
  200. {
  201.    char *p;
  202.  
  203.    /* Given a filename, we want to now set a current directory for the         */
  204.    /* Project.  This means that we need to split the name into a directory     */
  205.    /* and file portion.  We should also set the current directory.  Note that  */
  206.    /* It might be the case that the dmakefile has a directory override for the */
  207.    /* sources.  This will work out fine because we will use the full path for  */
  208.    /* the dmakefile.                                                           */
  209.    /* We want to set the symbols DIR and DMAKEFILE                             */
  210.    /* Start by saving the temporary copy of the new filename.                  */
  211.    strcpy(global.filename, buf);
  212.    p = strrchr(global.filename, '/');
  213.  
  214.    if (p == NULL) p = strrchr(global.filename, ':');
  215.    if (p != NULL)
  216.    {
  217.       char *tp;
  218.       int c;
  219.       BPTR dirlock;
  220.  
  221.       tp = p;
  222.       /* we need to include the colon in a root directory name, then    */
  223.       /* null terminate so we can get a lock on the project directory   */
  224.       if (*tp == ':') tp += 1;
  225.       c = *tp;
  226.       *tp = 0;
  227.       dirlock = xlockdir(global.filename);
  228.       if (dirlock)
  229.       {
  230.          UnLock(global.homedir);
  231.          global.homedir = dirlock;
  232.       }
  233.  
  234.       *tp = c;
  235.       Sym_Set(SYM_SCRIPT, p+1, NULL);
  236.    }
  237.    else
  238.    {
  239.       BPTR dirlock;
  240.       dirlock = xlockdir("");
  241.       if (dirlock)
  242.       {
  243.          UnLock(global.homedir);
  244.          global.homedir = dirlock;
  245.       }
  246.       Sym_Set(SYM_SCRIPT, global.filename, NULL);
  247.    }
  248.  
  249.    /* Now we want to update the title bar */
  250.    if (*global.filename)
  251.    {
  252.       int len;
  253.       strcpy(global.title, global.text[TEXT_PROJECT]);
  254.       len = strlen(global.title);
  255.       strncpy(global.title+len, Sym_Lookup(SYM_SCRIPT), MAX_TITLE-len);
  256.       global.title[MAX_TITLE-1] = 0;
  257.    }
  258.    else
  259.    {
  260.       strcpy(global.title, global.text[TEXT_NOPROJ]);
  261.    }
  262.  
  263.    SetWindowTitles(global.window, global.title, global.title2);
  264. }
  265.  
  266.  
  267. //***********************************************************************
  268. //* Procedure: expand_filename
  269. //* Synopsis:  expand_pathname(name)
  270. //* Purpose:   Constructs a fully expanded filename
  271. //*            Note, we can not assume that the file exists, so it will
  272. //*            not be possible to actually lock it.  We can assume that
  273. //*            the directory it is part of does exist.  It will just
  274. //*            return the name if the expansion fails in any way.
  275. //* Assumptions:  Buffer for fullname is at least large enough to hold
  276. //*            name, and at least MAX_FILENAME+1 bytes.  This routine 
  277. //*            will not return more than MAX_FILENAME bytes.
  278. //***********************************************************************
  279. void expand_filename(char *name, char *fullname)
  280. {
  281.    BPTR lock;
  282.    __aligned struct FileInfoBlock fib;
  283.    char *tail, *p;
  284.    int pos;
  285.    char buf[MAX_FILENAME+1];
  286.  
  287.    //
  288.    // Step 1 - split out any directory information from the actual name
  289.    //
  290.    p = strrchr(name, '/');
  291.    if (p == NULL) p = strrchr(name, ':');
  292.    if (p != NULL)
  293.    {
  294.       //
  295.       // There was some directory information involved
  296.       //
  297.       char c;
  298.       tail = p+1;
  299.       c = p[1];
  300.       p[1] = 0;
  301.       lock = Lock(name, SHARED_LOCK);
  302.       p[1] = c;
  303.    }
  304.    else
  305.    {
  306.       //
  307.       // No directory information involved, just the name relative to the
  308.       // current directory
  309.       //
  310.       lock = Lock("", SHARED_LOCK);
  311.       tail = name;
  312.    }
  313.  
  314.    //
  315.    // Step 2 - we have the lock on the directory and the tail part of the name
  316.    // We want to construct a fully qualified path for the directory.
  317.    // If for some reason the lock on the directory returned 0, we want to just
  318.    // return the name they gave us to begin with.
  319.    //
  320.    if (lock == 0)
  321.    {
  322.       strcpy(fullname, name);
  323.       return;
  324.    }
  325.  
  326.    //
  327.    // Step 3 - Fully qualify the directory portion into the buffer
  328.    //
  329.    if (DOSBase->dl_lib.lib_Version >= 36)
  330.    {
  331.       if (!NameFromLock(lock, buf, MAX_FILENAME))
  332.       {
  333.          //
  334.          // Either the name is too long or there was something else wrong with
  335.          // the file name, just return what they gave us as a start
  336.          //
  337.          UnLock(lock);
  338.          strcpy(fullname, name);
  339.          return;
  340.       }
  341.       UnLock(lock);
  342.       pos = 0;
  343.    }
  344.    else
  345.    {
  346.       // Running under 1.3, we have to do this the old fashion way
  347.  
  348.       //
  349.       // Just so we don't have to do any inserts/extra copies, we will work
  350.       // from the end of the buffer and insert as we go
  351.       //
  352.       pos = MAX_FILENAME-1;  // Leave room for a '/' on the end sometimes
  353.       buf[--pos] = 0;
  354.       while(lock != 0)
  355.       {
  356.          BPTR parent;
  357.          int len;
  358.  
  359.          //
  360.          // Examine the lock to get the name for it
  361.          //
  362.          Examine(lock, &fib);
  363.  
  364.          //
  365.          // Find the parent of this directory
  366.          //
  367.          parent = ParentDir(lock);
  368.          UnLock(lock);
  369.          lock = parent;
  370.  
  371.          len = strlen(fib.fib_FileName);
  372.          pos -= 1;
  373.  
  374.          if (len > pos)
  375.          {
  376.             //
  377.             // oops, not enough room, just return the name they gave us
  378.             //
  379.             UnLock(lock);
  380.             strcpy(fullname, name);
  381.             return;
  382.          }
  383.          buf[pos] = lock ? '/' : ':';
  384.          pos -= len;
  385.          memcpy(buf+pos, fib.fib_FileName, len);
  386.       }
  387.    }
  388.  
  389.    //
  390.    // We have the path part in the buffer and the name part in the tail
  391.    // All that is left is to concatenate them together correctly
  392.    //
  393.    {
  394.       int len;
  395.  
  396.       //
  397.       // Successful, the buf holds the path for the directory.  We will need
  398.       // to add a / to the end if it doesn't end in a colon
  399.       //
  400.       len = strlen(buf+pos);
  401.       if ((buf[pos+len-1] != ':') && (buf[pos+len-1] != '/'))
  402.       {
  403.          buf[pos+len++] = '/';
  404.          buf[pos+len] = 0;
  405.       }
  406.  
  407.       if ((len + strlen(tail)) >= MAX_FILENAME)
  408.       {
  409.          //  Oops, it's to long, just use the name
  410.          strcpy(fullname, name);
  411.       }
  412.       else
  413.       {
  414.          //  Tack the name onto the path
  415.          strcpy(fullname, buf+pos);
  416.          strcpy(fullname+len, tail);
  417.       }
  418.    }
  419.    return;
  420. }
  421.  
  422.  
  423. /***********************************************************************************
  424.  * Procedure: read_script
  425.  * Synopsis:  read_script()
  426.  * Purpose:   Read in a config script
  427.  ***********************************************************************************/
  428. void read_script()
  429. {
  430.    char buf[256];
  431.    FILE *fp;
  432.    char *val;
  433.    int len;
  434.  
  435.    go_dir(global.homedir);
  436.  
  437.    val = Sym_Lookup("TYPE");
  438.    if (!*val) val = "Normal";
  439.  
  440.    if ((!strchr(val, '/')) && (!strchr(val, ':')))
  441.    {
  442.       sprintf(buf, "DCC:CONFIG/%s.DMakefile", val);
  443.       val = buf;
  444.    }
  445.  
  446.    fp = fopen(val, "r");
  447.    if (fp == NULL)
  448.    {
  449.       request(1, TEXT_BADFILE, val, NULL);
  450.       return;
  451.    }
  452.  
  453.    Sym_Set(SYM_HOLD, "", NULL);
  454.    while((len = fread(buf, 1, 255, fp)) > 0)
  455.    {
  456.       buf[len] = 0;
  457.       Sym_Set(SYM_HOLD, NULL, buf);
  458.    }
  459.    fclose(fp);
  460. }
  461.  
  462. /***********************************************************************************
  463.  * Procedure: read_file
  464.  * Synopsis:  rc = read_file()
  465.  * Purpose:   Read in a dmakefile
  466.  ***********************************************************************************/
  467. int read_file()
  468. {
  469.    char buf[256];
  470.  
  471.    FILE *fp;
  472.    int cstate, len;
  473.    char name[64];
  474.    char *fname;
  475.  
  476.    fname = Sym_Lookup(SYM_SCRIPT);
  477.  
  478.    go_dir(global.homedir);
  479.  
  480.    fp = fopen(fname, "r");
  481.    if (fp == NULL)
  482.    {
  483.       request(1, TEXT_BADFILE, fname, NULL);
  484.       return(1);
  485.    }
  486.    cstate = 1;
  487.    while(cstate && (fgets(buf, 256, fp) != NULL))
  488.    {
  489.       int len;
  490.       int extend;
  491.       char *p, *val;
  492.  
  493.       len = strlen(buf)-1;
  494.  
  495.       /* Skip any blank lines */
  496.       if (len < 2) continue;
  497.  
  498.       /* Ignore any comment lines (note that a # at the start of a line where the */
  499.       /* previous line had a continuation mark is not considered a comment)       */
  500.       if (buf[0] == '#' && cstate == 1)
  501.       {
  502.          /* When we get to a line that says:                                      */
  503.          /*    #### AUTOMATICALLY GENERATED - DO NOT EDIT BELOW THIS LINE         */
  504.          /* we want to just exit the loop and not gather any more lines           */
  505.          if (!memcmp(buf, "#### AUTOMATICALLY GENERATED", 28)) break;
  506.          continue;
  507.       }
  508.  
  509.       extend = 0;
  510.       buf[len] = 0; /* Wipe out the \n */
  511.       if (buf[len-1] == '\\')
  512.       {
  513.          extend = 1;
  514.          buf[--len] = 0;
  515.       }
  516.  
  517.       if (cstate == 2)
  518.       {
  519.          /* This was a continuation line, just append it to the */
  520.          /* Current string.                                     */
  521.          Sym_Set(name, NULL, buf);
  522.       }
  523.       else
  524.       {
  525.          /* We need to parse out the name */
  526.          p = strchr(buf, '=');
  527.          if (p == NULL)
  528.          {
  529.             /* Funny line, we should issue a warning about it and just */
  530.             /* Ignore it for now                                       */
  531.          }
  532.          else
  533.          {
  534.             /* Parse out the name that is being assigned to */
  535.             val = p+1;
  536.  
  537.             /* Remove any trailing blanks from the name */
  538.             while(p > buf && p[-1] == ' ') p--;
  539.             *p = 0;
  540.             /* Skip over any leading blanks after the '=' */
  541.             while(*val == ' ') val++;
  542.             p = buf;
  543.  
  544.             /* Remove any leading blanks from the name */
  545.             while(*p == ' ') p++;
  546.             len = strlen(p);
  547.  
  548.             /* We now have p pointing to the nul terminated name and val pointing */
  549.             /* to a null terminated substitution string for the variable          */
  550.             if (len > 63)
  551.             {
  552.                /* The name is too long, we should issue a warning and truncate it */
  553.                p[63] = 0;
  554.             }
  555.             strcpy(name, p);
  556.             /* We might consider looking up the name to see if this is a redefinition */
  557.             /* For now we will be silent about it.                                */
  558.             Sym_Set(name, val, NULL);
  559.          }
  560.       }
  561.       cstate = 1;
  562.       if (extend) cstate = 2;
  563.    }
  564.    /* Now we also want to gather in the automatically generated stuff so that      */
  565.    /* we don't have to read it in again.  This would only need to be dealt with    */
  566.    /* If they actually change the type of project that we are building.  This will */
  567.    /* also have the effect of retaining any special configuration that they have   */
  568.    /* possibly created on another machine.                                         */
  569.    Sym_Set(SYM_HOLD, "", NULL);
  570.    while((len = fread(buf, 1, 255, fp)) > 0)
  571.    {
  572.       buf[len] = 0;
  573.       Sym_Set(SYM_HOLD, NULL, buf);
  574.    }
  575.    fclose(fp);
  576.  
  577.    go_dir(global.homedir);
  578.    UnLock(global.workdir);
  579.    global.workdir = xlockdir(Sym_Lookup("DIR"));
  580.    return(0);
  581. }
  582.  
  583. /***********************************************************************************
  584.  * Procedure: write_file
  585.  * Synopsis:  write_file()
  586.  * Purpose:   Write out a dmake file based on the current options
  587.  ***********************************************************************************/
  588. int write_file()
  589. {
  590.    FILE *fp;
  591.    char *place;
  592.    char *name, *val;
  593.    char *fname;
  594.  
  595.    go_dir(global.homedir);
  596.  
  597.    fname = Sym_Lookup(SYM_SCRIPT);
  598.  
  599.    name = Sym_Lookup("PROJECT");
  600.    if (!*name) name = "*"; /* Force an invalid project name */
  601.  
  602.    while(*name)
  603.    {
  604.       if (strchr("#?\\/*: []{}^$&|()\"'", *name))
  605.       {
  606.          request(1, TEXT_BADPROJ, Sym_Lookup("PROJECT"), NULL);
  607.          return(1);
  608.       }
  609.       name++;
  610.    }
  611.  
  612.    if(!*fname)
  613.    {
  614.       request(1, TEXT_NOPROJ, NULL , NULL);
  615.       return(1);
  616.    }
  617.  
  618.    while ((fp = fopen(fname, "w")) == NULL)
  619.    {
  620.       struct stat stat_buf;
  621.  
  622.       if ( (stat(fname, &stat_buf) >= 0) &&
  623.            (!(stat_buf.st_mode & S_IWRITE)))
  624.       {
  625.          /* The file exists, but is read-only.  See if they want us */
  626.          /* to check it out for them to work on                     */
  627.          if (!request(0, TEXT_SCRIPTCO, fname, NULL)) return;
  628.  
  629.          /* We need to check the file out for them */
  630.          exec_command(global.text[CONFIG_CO], fname);
  631.       }
  632.       else
  633.       {
  634.          request(0, TEXT_BADFILE, fname, NULL);
  635.          return(1);
  636.       }
  637.  
  638.    }
  639.  
  640.    place = NULL;
  641.    while(place = Sym_Next(place, &name, &val))
  642.    {
  643.       int nlen, vlen;
  644.  
  645.       nlen = strlen(name);
  646.       vlen = strlen(val);
  647.  
  648.       if (nlen + vlen < LINE_LEN)
  649.       {
  650.          if (fprintf(fp, "%s= %s\n", name, val) <= 0) goto ioerr;
  651.       }
  652.       else
  653.       {
  654.          if (fputs(name, fp) || fputs("= ", fp)) goto ioerr;
  655.          nlen += 2;
  656.          while(nlen + vlen > LINE_LEN)
  657.          {
  658.             /* Attempt to break the line at a space - DMAKE doesn't require   */
  659.             /* that the line end in a space, but it is easier for a person to */
  660.             /* edit it when it works out that way.                            */
  661.             nlen = LINE_LEN-nlen;
  662.             while((nlen > MIN_LINE) && val[nlen-1] != ' ') nlen--;
  663.  
  664.             /* If we couldn't find a space on the line, just break it at the */
  665.             /* target column anyways.                                        */
  666.             if (nlen == MIN_LINE) nlen = LINE_LEN-nlen;
  667.  
  668.             if (fwrite(val, nlen, 1, fp) != 1) goto ioerr;
  669.             if (fputs("\\\n", fp)) goto ioerr;
  670.             val += nlen;
  671.             vlen -= nlen;
  672.             nlen = 0;
  673.          }
  674.          if (fputs(val, fp) || (fputc('\n', fp) == EOF)) goto ioerr;
  675.       }
  676.    }
  677.    /* Add the separator line                                                */
  678.    if (fputs("\n#### AUTOMATICALLY GENERATED - DO NOT EDIT BELOW THIS LINE\n", fp))
  679.       goto ioerr;
  680.    if (fputs(Sym_Lookup(SYM_HOLD), fp)) goto ioerr;
  681.    fclose(fp);
  682.  
  683.    /* Now we need to create an icon for it */
  684.  
  685.    {
  686.       struct DiskObject *dobj = NULL;
  687.  
  688.       if (global.oldproject)
  689.          /* We read the project, so it may have a valid existing icon       */
  690.          dobj = GetDiskObject(fname);
  691.       if (dobj != NULL)
  692.       {
  693.          int len;
  694.          len = strlen(dobj->do_DefaultTool);
  695.          if ((len >= 5) && (!stricmp(dobj->do_DefaultTool+len-5, "vmake")))
  696.             ;
  697.          else
  698.          {
  699.             /* Don't trust it, make a new one                               */
  700.             FreeDiskObject(dobj);
  701.             dobj = NULL;
  702.          }
  703.       }
  704.       if (dobj == NULL)
  705.       {
  706.          dobj = GetDiskObject("DCC:Config/Default_Project");
  707.          if (dobj != NULL)
  708.             PutDiskObject(fname,dobj);
  709.       }
  710.       if (dobj != NULL)
  711.          FreeDiskObject(dobj);
  712.    }
  713.    return(0);
  714.  
  715. ioerr:
  716.    request(1, TEXT_IOERR, fname, NULL);
  717.    fclose(fp);
  718.    return(1);
  719. }
  720.  
  721.